package org.amse.korovkin.ciphering.view;

import org.amse.korovkin.ciphering.model.cipher.monocipher.MonoAttacker;
import java.awt.event.*;
import java.io.*;
import java.util.zip.DataFormatException;
import javax.swing.*;
import org.amse.korovkin.ciphering.Main;
import org.amse.korovkin.ciphering.model.*;
import org.amse.korovkin.ciphering.view.MyJDialog.*;

public class CipherActions {
    public static final int LOAD_MODE = 1;
    public static final int OPEN_MODE = 2;
    private static final String DECRYPT_ICON_PATH = "org/amse/korovkin/ciphering/icons/decrypt-24.png";
    private static final String ENCRYPT_ICON_PATH = "org/amse/korovkin/ciphering/icons/encrypt-24.png";
    private static final String ATTACK_ICON_PATH = "org/amse/korovkin/ciphering/icons/attack-24.png";

    public static class DecryptAction extends AbstractAction {
        private CipheringJFrame myParent;
        public DecryptAction(CipheringJFrame jFrame) {
            myParent = jFrame;
            putValue(NAME, "Decrypt");
            putValue(SHORT_DESCRIPTION, "Decrypt Source Text");
            putValue(SMALL_ICON, null);
            ClassLoader cl = Main.class.getClassLoader();
            putValue(LARGE_ICON_KEY, new ImageIcon(cl.getResource(DECRYPT_ICON_PATH)));
        }
        public void actionPerformed(ActionEvent e) {
            String filePaths[] = new String[3];
            filePaths[0] = myParent.getSrcFilePath();

            if (filePaths[0] == null) {
                JOptionPane.showMessageDialog(myParent, "Please choose SourceFile", "Error", JOptionPane.ERROR_MESSAGE);
                return;
            }
            filePaths[1] = addPrefix(filePaths[0], "dec");
            if (filePaths[1] == null || filePaths[1].length() == 0) {
                JOptionPane.showMessageDialog(myParent, "Please choose DecryptedFile", "Error", JOptionPane.ERROR_MESSAGE);
                return;
            }
            KeyJDialog keyJDialog = new KeyJDialog(myParent);
            int returnValue = keyJDialog.getCloseOparetion();
            if (returnValue == -1) {
                return;
            }
            filePaths[2] = myParent.getKeyPath();
            if (filePaths[2] == null || filePaths[2].length() == 0) {
                JOptionPane.showMessageDialog(myParent, "Please choose KeyFile"
                        , "Error", JOptionPane.ERROR_MESSAGE);
                return;
            }
            try {
                IModel model = Model.getInstance();
                model.decrypt(filePaths[0], filePaths[1], filePaths[2]);
                JOptionPane.showMessageDialog(myParent, "Decrypt successful");
                myParent.setDstText(filePaths[1]);
            } catch (IOException ex) {
                JOptionPane.showMessageDialog(myParent, ex.getMessage(), "Error"
                        , JOptionPane.ERROR_MESSAGE);
                return;
            } catch (ArrayIndexOutOfBoundsException ex) {
                JOptionPane.showMessageDialog(myParent, ex.getMessage()
                        + "Please choose another KeyFile.", "Error", JOptionPane.ERROR_MESSAGE);
                return;
            }
        }
    }
    public static class EncryptAction extends AbstractAction {
        private CipheringJFrame myParent;
        public EncryptAction(CipheringJFrame jFrame) {
            myParent = jFrame;
            putValue(NAME, "Encrypt");
            putValue(SHORT_DESCRIPTION, "Encrypt Source Text");
            ClassLoader cl = Main.class.getClassLoader();
            putValue(LARGE_ICON_KEY, new ImageIcon(cl.getResource(ENCRYPT_ICON_PATH)));
        }
        public void actionPerformed(ActionEvent e) {
            String filePaths[] = new String[3];
            filePaths[0] = myParent.getSrcFilePath();

            if (filePaths[0] == null) {
                JOptionPane.showMessageDialog(myParent, "Please choose SourceFile"
                        , "Error", JOptionPane.ERROR_MESSAGE);
                return;
            }
            filePaths[1] = addPrefix(filePaths[0], "enc");
            if (filePaths[1] == null || filePaths[1].length() == 0) {
                JOptionPane.showMessageDialog(myParent, "Please choose EncryptFile"
                        , "Error", JOptionPane.ERROR_MESSAGE);
                return;
            }
            KeyJDialog keyJDialog = new KeyJDialog(myParent);
            int returnValue = keyJDialog.getCloseOparetion();
            if (returnValue == -1) {
                return;
            }
            filePaths[2] = myParent.getKeyPath();
            if (filePaths[2] == null || filePaths[2].length() == 0) {
                JOptionPane.showMessageDialog(myParent, "Please choose KeyFile"
                        , "Error", JOptionPane.ERROR_MESSAGE);
                return;
            }
            try {
                IModel model = Model.getInstance();
                model.encrypt(filePaths[0], filePaths[1], filePaths[2]);
                JOptionPane.showMessageDialog(myParent, "Encrypt successful");
                myParent.setDstText(filePaths[1]);
            } catch (IOException e1) {
                JOptionPane.showMessageDialog(myParent, e1.getMessage()
                        , "Error", JOptionPane.ERROR_MESSAGE);
                return;
            } catch (ArrayIndexOutOfBoundsException e2) {
                JOptionPane.showMessageDialog(myParent, e2.getMessage()
                        + "Please choose another KeyFile.", "Error"
                        , JOptionPane.ERROR_MESSAGE);
                return;
            }
        }
    }
    public static class AttackAction extends AbstractAction {
        private CipheringJFrame myParent;
        public AttackAction(CipheringJFrame jFrame) {
            myParent = jFrame;
            putValue(NAME, "Attack");
            putValue(SHORT_DESCRIPTION, "Attack Source Text");
            ClassLoader cl = Main.class.getClassLoader();
            putValue(LARGE_ICON_KEY, new ImageIcon(cl.getResource(ATTACK_ICON_PATH)));
        }
        public void actionPerformed(ActionEvent e) {
            int key = OPEN_MODE;
            String filePaths[] = new String[3];
            filePaths[0] = myParent.getSrcFilePath();

            if (filePaths[0] == null) {
                JOptionPane.showMessageDialog(myParent, "Please choose File to attack", "Error", JOptionPane.ERROR_MESSAGE);
                return;
            }
            filePaths[1] = addPrefix(filePaths[0], "res");
            if (filePaths[1] == null || filePaths[1].length() == 0) {
                JOptionPane.showMessageDialog(myParent, "Please choose OutputFile", "Error", JOptionPane.ERROR_MESSAGE);
                return;
            }
            StatisticJDialog statisticJDialog = new StatisticJDialog(myParent);
            int statisticResult = statisticJDialog.getCloseOparetion();
            switch(statisticResult) {
                case StatisticJDialog.CANCEL:
                    {return;}
                case StatisticJDialog.LOAD_MODE: {
                    key = CipherActions.LOAD_MODE;
                    break;
                }
                case StatisticJDialog.OPEN_MODE: {
                    break;
                }
                default:
                    {throw new UnsupportedOperationException("Wrong returnValue from StatisticJDialog");}
            }
            filePaths[2] = myParent.getStatisticPath();
            if (filePaths[2] == null || filePaths[2].length() == 0) {
                JOptionPane.showMessageDialog(myParent, "Please choose StatisticFile", "Error", JOptionPane.ERROR_MESSAGE);
                return;
            }
            AttackJDialog attackJDialog = new AttackJDialog(myParent);
            Thread attackThread = new AttackThread(myParent, attackJDialog, key);
            IModel model = Model.getInstance();
            model.addListener(attackJDialog);
            attackThread.start();
            attackJDialog.setVisible(true);
            int attackResult = attackJDialog.getResult();
            if (attackResult == AttackJDialog.UNSUCCESSFUL) {
                model.setStopFlag(true);
            }
        }
    }
    public static class AttackThread extends Thread {
        private CipheringJFrame myParent;
        private AttackJDialog son;
        private int mode;
        public AttackThread(CipheringJFrame jFrame, AttackJDialog jDialog, int key) {
            myParent = jFrame;
            son = jDialog;
            mode = key;
        }
        @Override
        public void run() {
            String filePaths[] = new String[3];
            filePaths[0] = myParent.getSrcFilePath();
            filePaths[1] = addPrefix(filePaths[0], "res");
            filePaths[2] = myParent.getStatisticPath();
            switch (mode) {
                case OPEN_MODE: {
                    try {
                        IModel model = Model.getInstance();
                        model.createEtalonStatistic(filePaths[2]);
                        System.out.println("Statistic created successful");
                    } catch (IOException ex) {
                        JOptionPane.showMessageDialog(myParent, ex.getMessage()
                                + "Please choose correct StatisticFile.", "Error", JOptionPane.ERROR_MESSAGE);
                        return;
                    } catch (DataFormatException ex) {
                        JOptionPane.showMessageDialog(myParent, ex.getMessage()
                                + "Please choose correct StatisticFile.", "Error", JOptionPane.ERROR_MESSAGE);
                        return;
                    } catch (ArrayIndexOutOfBoundsException ex) {
                        JOptionPane.showMessageDialog(myParent, ex.getMessage()
                                + "Please choose StatisticFile correctly.", "Error", JOptionPane.ERROR_MESSAGE);
                        return;
                    }
                    break;
                }
                case LOAD_MODE: {
                    Statistic.copyStatistic(filePaths[2]);
                    break;
                }
                default:
                    {throw new UnsupportedOperationException("Wrong MODE_VALUE for AttackThread");}
            }

            int attackResult;
            try {
                IModel model = Model.getInstance();
                attackResult = model.attack(filePaths[0], filePaths[1]);
                if (attackResult != MonoAttacker.SUCCESSFUL) {
                    return;
                }
                JOptionPane.showMessageDialog(myParent, "Attack successful");
                son.dispose();
                model.removeListener(son);
                myParent.setDstText(filePaths[1]);
            } catch (IOException ex) {
                JOptionPane.showMessageDialog(myParent,ex.getMessage()
                        , "Error", JOptionPane.ERROR_MESSAGE);
                son.dispose();
                IModel model = Model.getInstance();
                model.removeListener(son);
                return;
            } catch (DataFormatException ex) {
                        JOptionPane.showMessageDialog(myParent,ex.getMessage()
                        + "Please choose correct SourceFile."
                        , "Error", JOptionPane.ERROR_MESSAGE);
                son.dispose();
                IModel model = Model.getInstance();
                model.removeListener(son);
                return;
            } catch (InterruptedException ex) {
                ex.printStackTrace();
                son.dispose();
                IModel model = Model.getInstance();
                model.removeListener(son);
                return;
            } catch (ArrayIndexOutOfBoundsException e2) {
                JOptionPane.showMessageDialog(myParent, e2.getMessage()
                        + "Please choose StatisticFile correctly.", "Error", JOptionPane.ERROR_MESSAGE);
                son.dispose();
                IModel model = Model.getInstance();
                model.removeListener(son);
                return;
            }
        }
    }
    private static String addPrefix(String filePath, String prefix) {
        String prefOutFile = "";
        if (filePath.contains(".")) {
            int index = filePath.lastIndexOf(".");
            String buf = filePath.substring(0, index);
            prefOutFile = buf.concat("_" + prefix + filePath.substring(index, filePath.length()));
        } else {
            prefOutFile = filePath.concat("_" + prefix);
        }
        return prefOutFile;
    }

}
